This section describes all the common file formats in the field of Commodore 64 emulation. It is intended for the interested programmer, who might wish to learn the internal workings of the emulator. If you are only interested in playing your favorite C64 game, then you do not need to worry about these details.
C.1 *.P00 Program Format
The *.P00 format is a flexible file format, that is able to support all common types of C64 files. As it has a magic constant at the head of the file it also enables the emulator to make sure that no invalid files are used. This makes it the recommended file format for single files.
typedef struct
{
Byte P00Magic[8]; /* $00 - $07 */
Byte OrigFName[17]; /* $08 - $18 */
Byte RecordSize; /* $19 */
Byte Data[n]; /* $1A - ... */
} P00File;
P00Magic - Magic constant 'C64File' =
{$43, $36, $34, $46, $69, $6C, $65, $00}
OrigFName - Original C64 filename (CBM Charset)
RecordSize - Record size for RELative files
Data - The actual data
This file format was first used by Wolfgang Lorenz in PC64. Note that the extension *.P00 is not fix. rather the 'P' stands for 'PRG' and can become an 'S' for 'SEQ', or 'R' for 'REL'. Furthermore the '00' can be used count to '01','02','03'... to resolve name conflicts caused by truncation a 16 character C64 filename to a 8+3 MS-DOS name. Of course this is not relevant on a Macintosh, it's just a hint if you should ever happen to run across a *.P01 file.
C.2 *.C64 Program Format
The *.C64 format is the exact format that the original Commodore 64 uses to store a programs to either disk or tape.
typedef struct
{
Byte LoadAddrLow, LoadAddrHigh; /* $00 - $01 */
Byte PrgData[n]; /* $02 - ... */
} C64File;
LoadAddr - The address in C64 RAM at which the program will get stored when loaded with secondary device number 1.
PrgData - The actual program.
This file format was introduced to the world of emulators by Miha Peternel in C64S. As can be seen from the above description this is a rather simple file format. Still it's use is not recommended as it does not provide the emulator with any possibility to make sure that only valid files are used.
C.3 *.X64 Floppy Disk Image Format
The *.X64 format is flexible format for disk image files that supports a wide range of floppy disks. Note so, that the implementation of *.X64 in Power64 can only handle 1541 disks.
TrackCnt - Number of Tracks on the Disk (Side 0) = {35}
SecondSide - Is it a Double Sided Disks (0..No, 1..Yes) = {$00}
ErrorFlag - Flag for Error (precise meaning unclear) (unused)
Reserved - Must be $00
DiskInfo - Description of the Disk Image (in ASCII or ISO Latin/1)
ConstZero - Must be $00
DiskImage - 683 disk sectors of 256 bytes each.
(For more information on DiskImage see also: *.D64)
The *.X64 disk image format was originally created by Teemu Rantanen for use in X64.
As *.X64 uses a well defined header the emulator is able to make sure that only valid files are used. This is a clear advantage over the (unfortunately more widespread) *.D64 format.
C.4 *.D64 Floppy Disk Image Format
The *.D64 file format is a 1:1 copy of all sectors as they appear on a floppy disk. On a C1541 formatted disk (and thus also in a *.D64 file) each sector consists of 256 Byte. In order to be able to deal with bad sectors the *.D64 format optionally features an additional error byte per sector. A disk that was formatted by using the standard C1541 command NEW will have 35 tracks containing a total of 683 sectors. With the use of special software it is possible to format and write disks with up to 40 tracks. While the extra tracks are not within the original specification such disks are readable with most C1541 drives (Note: Reading does not require special software.).
Currently there are four different types of *.D64 formats:
ΓÇó 683 Sectors (= 35 Tracks) without error info
(Total Size: 174848 Byte = 170.75 KByte)
ΓÇó 683 Sectors (= 35 Tracks) with error info
(Total Size: 175531 Byte = 171.42 KByte)
ΓÇó 768 Sectors (= 40 Tracks) without error info
(Total Size: 196608 Byte = 192 KByte)
ΓÇó 768 Sectors (= 40 Tracks) with error info
(Total Size: 197376 Byte = 192.75 KByte)
Note that tracks at the outside edge of the disk contain more sectors than those near to the disks center (Zone Bit Recording). The number of sectors on each track is:
Tracks 1..17 - 21 Sectors
Tracks 18..24 - 19 Sectors
Tracks 25..30 - 18 Sectors
Tracks 31..35 - 17 Sectors
Tracks 36..40 - 17 Sectors (non standard!)
The Block Allocation Map (BAM) is stored on track 18 - sector 0; the directory starts at track 18 - sector 1.
For storage in a *.D64 image the sectors are arranged in the followingway:
Track 1 - Sector 0: Offset 0 * 256
Track 1 - Sector 1: Offset 1 * 256
.....
Track 1 - Sector 20: Offset 20 * 256
Track 2 - Sector 0: Offset 21 * 256
Track 2 - Sector 1: Offset 22 * 256
.....
Track 2 - Sector 20: Offset 41 * 256
Track 3 - Sector 0: Offset 42 * 256
.....
Track 18 - Sector 0: Offset 357 * 256
Track 18 - Sector 1: Offset 358 * 256
.....
Track 35 - Sector 0: Offset 666 * 256
Track 35 - Sector 1: Offset 667 * 256
.....
Track 35 - Sector 16: Offset 682 * 256
Note that tracks are counted starting with track 1, while sector numbers start with 0.
typedef struct
{
Byte DiskImage[SectorCnt][256];
Byte ErrorInfo[SectorCnt]; /* Optional */
} D64File;
DiskImage - 256 Bytes per Sector
ErrorInfo - 1 Byte per Sector
Note that the error information for all sectors is concentrated at the end of the file. There is no interweaving of disk image data and error information on a per sector basis.
The meaning of the ErrorInfo is given in the following table:
Code Error Type 1541 error description
---- ----- ---- ------------------------------
01 00 N/A No error, sector ok.
02 20 Read Header block not found
03 21 Seek No sync character
04 22 Read Data block not present
05 23 Read Checksum error in data block
06 24 Write Write verify (on format)
07 25 Write Write verify error
08 26 Write Write protect on
09 27 Seek Checksum error in header block
0A 28 Write Write error
0B 29 Seek Disk ID mismatch
0F 74 Read Disk Not Ready (no device 1)
C.5 ZipCode File Format (1!*, 2!*, etc.)
This is a compressed version of a D64 file that is frequently found on C64 sites that have already been around for a long time (and have served C64 owners before emulation became common). A single D64 file is split into 4 (35 tracks) or 5 (40 tracks) segments. Each part is then packed using the simple (and poorly compressing) Run Length Encoding compression method.
The primary reason for the existance of this file format is that every segment of such a ZipCode File is less than 44 KByte in size, and can thus easily be handled by a real C64 and its 1541 disk drive (say after a transmittion via modem from a BBS). Furthermore packing and unpacking are very fast on a C64.
Modern computers, like a Power Macintosh, that are used to run C64 emulators have no problem handling files 171KByte in size (like D64 files), so they do not benefit from splitting one 'large' file in several smaller. In fact, due to the large allocations units of modern hard disks and CD-ROMs the space wasted by 4 half filled last blocks of the 4 files is likely to exceeds the savings from compression for reasonable well filled floppy disks. Furthermore modern compression formats like Zip, GZ, LHA or SIT offer much besser compression that the RLE used in the ZipCode.
While ZipCode is not the best choice of a file format for an emulator it is still a good format on a real C64 and many sites offer their files in ZipCode format to support the faithful C64-addicts that stick to the real hardware.
Note that there are also two other Zip formats for the C64, that are not supported by Power64. They use the filename-prefixes 1!!, 2!!, 3!!… and A!, B!, C!… respectively. They are described in the File Format collection of Peter Schepers the author of 64COPY (schepers@dcs1.uwaterloo.ca), but I have never seen files in either format anywhere on the web.
The contents of the floppy disk is devided onto the 4 or 5 segments accoring to the following table:
FileName Track Range Block Count
-------- ----------- -----------
1!xxxxxx 1 - 8 168 Sectors
2!xxxxxx 9 - 16 168 Sectors
3!xxxxxx 17 - 25 172 Sectors
4!xxxxxx 26 - 35 175 Sectors
5!xxxxxx 36 - 40 85 Sectors (only for 40 track disk images)
All segments have a similar structure based on compressed disk sectors. Note that the first segments header is slightly different from the rest.
Compression takes place on a sector per sector basis. For each sector there is the following structure:
typedef struct
{
Byte Track; /* Track Number and Compression Mode */
Byte Sector; /* Sector Number */
union {
{ Byte NoCompression[256]; } Mode00;
{ Byte SingleByte; } Mode01;
{ Byte Length;
Byte RepeatChar;
Byte RLEData[Length]; } Mode10;
} Data;
} ZipSector;
Track gives the track number of the compressed sector in Bit 5 to Bit 0. Bits 7&6 give the compression mode.
Sector gives the sector number of the compressed sector.
The meaning of Data depends on the compression mode:
Compression Modes:
00 - No compression; The sector is stored in full. The 256 Data Bytes give the sectors contents.
01 - The entire sector is filled with a single Byte. A single Data Byte will be repeated 256 times to fill the sector.
10 - Run Length Encoded Sector - Length gives the number of Bytes of compressed RLEData. RepeatChar is a Byte that does not appear in the original sector and that is now used as a marker for runlength encoded pieces of data. RLEData different from RepeatChar is just plain, uncompressed data, that can be directly copied. After RepeatChar is encountered, the next Byte of RLEData gives the Length of the Run and
a third Byte denotes the Byte to be repeated.
11 -Unused
The sectors of a track are NOT stored in a linear fashion (1, 2, 3, 4…). To make packing and unpacking much faster on a real 1541 interleaving is used. The proper sequence of sectors depends on the number of sectors in a track and can be seen below.
The Lynx (or LNX or Ultimate Lynx) file format was developed by Will Corley for use on the C64(!). It is designed around blocks of 254 Bytes.This corresponds to the 256 Bytes of a 1541 disk sector minus the 2 Bytes that contain the next track/sector information and makes it easy to copy files between 1541 disks and Lynx files.
Unfortunately the Lynx header is written in a format that is somewhat harder to handle than that of a T64 or D64 file, and thus the format has received little attention by emulator authors.
typedef struct
{
Byte Directory[DirSectorCnt * 254];
Byte Data[DataSectorCnt * 254];
} LynxFile;
The Directory starts out with a small BASIC program which, when loaded and run, displays the message "Use LYNX to dissolve this file". The actual message and size of the program can change. Usually, its 94 bytes long, from $0000 to $005D. Note that some emulators depend on the exact size of 94 Bytes and also require that the text 'LYNX' can be found at offset $003C to $003F.
Following this is a <CR> and the number of blocks of the directory in ASCII with spaces on both sides. For a directory with just 1 block that would be:
$005E: 0x0D, 0x20, 0x31, 0x20, 0x20 /* . 1 */
After this there is the "signature" of the archive, an CBM lower case (ASCII for the most part) text ending in <CR> that describes the Lynx archive. Normally this signature will contain the string 'LYNX'.
This is followed by the number of files in the Lynx archive, as before this is given as a ASCII number surrounded by spaces and delimited by <CR>.
For a directory with 3 files that would be:
$007C: 0x20, 0x33, 0x20, 0x0D /* 3 . */
Following these headers is the actual directory. Each file is described by its filename (in PETASCII, often padded to 16 characters by shifted-spaces), followed by the size of the file (plus 2 bytes for the address) in blocks of 254 bytes, the file type (P, S, R, U), the size in bytes of the last block. If the file type is Relative, this entry is the Record size, and the next entry is the last block size. If the file type is not Relative, the next entry will be the next filename. Relative files are not supported by Power64. Every entry is terminated by a <CR>, numbers are surrounded by spaces. For example this could be:
The actual files follow the directory. Each file consists of 2 bytes load address plus the actual data.
Note that the directory and every files size is a multiple of 254 bytes. If the actual information requires less space, then there is a wasted gap, that is filled with 0x00 in Power64 (other implementations vary).
In the above example the directory consists of just one block, and thus Block Out starts at offset 1 * 254. The second file (Serpentine) starts at offset (1+10) * 254 and Quadromania begins at (1+10+35) * 254.
The *.T64 file format is a very well designed (so in former times poorly documented) format, developed by Miha Peternel for C64S. Like a disk image file it is able to contain many logical files; a great help when keeping lots of files organized. On the other hand there is very little organizational overhead, both in terms of lost space on the host file system and in terms loading time for the emulator. As a *.T64 file contains a magic header it is furthermore possible for the emulator to prevent invalid files from being used.
typedef struct
{
TapeHeader Header; /* $0000 - $003F */
TapeEntry Entry[MaxFiles]; /* $0040 - ($03FF) */
Byte Data[n]; /* ($0400) - ... */
} T64File;
Header - General information; containing a magic string to identify a *.T64 file, the number of files on the tape etc... (see below for details)
Entry - Tape Directory; A list of all files stored on the tape. The value MaxFiles is defined in Header, but is usually 30. (see below for details)
To prevent error messages all other values are also treated
a relocateable programs.
StartAddr - Start of the destination range in C64 memory. (Low/High)
EndAddr - End of the destination range in C64 memory. (Low/High)
ReservedA - Must be $00
TapePos - Offset from the start of the tape file to the logical
files data
ReservedB - Must be $00
FileName - filename (CBM character set)
There was very little official documentation on the *.T64 file format. What is known was generally learned by examining existing *.T64 files. Sometimes that was not done with sufficient scrutiny. Thus there are many *.T64 files on the internet that do not follow the described format. It is, for example quite common, that the difference between EndAddr and StartAddr of a file is not consistent with the file size of the *.T64 file. Power64 attempts to automatically fix such inconsistencies.
As stated above there are some emulators that rely on the fact that there are always exactly 30 directory entries, while at the same time they will always load the first file. Please keep this in mind when making Commodore 64 files available to a general public that might use a wide range of emulators.
All multi-byte values are stored in the Little-Endian Format (Low/High) that is generally used by the C64.
C.8 RAM Snapshot File
The format for RAM Snapshots was adapted from a suggestion made by Jouko Valta for the Vice-Emulator on Unix systems. The original description, that explains the meaning of the individual header fields in more detail, can be found at: http://stekt.oulu.fi:/~jopi/x64/RAM-format
typedef struct
{
Byte MagicCode[6]; /* $00 - $05 */
Byte ReservedA[2]; /* $06 - $07 */
Byte Version[2]; /* $08 - $09 */
Byte EmulatorID; /* $0A */
Byte SnapType; /* $0B */
Byte RAMStart[2]; /* $0C - $0D */
Byte RAMSizeKB[2]; /* $0E - $0F */
Byte REUSizeKB[2]; /* $10 - $11 */
Byte ReservedB[14]; /* $12 - $1F */
Byte Config[6]; /* $20 - $25 */
Byte IORAM; /* $26 */
Byte CPURestore; /* $27 */
Byte PCHigh, PCLow; /* $28 - $29 */
Byte RegA, RegX, RegY; /* $2A - 2C */
Byte RegFlags, RegSP; /* $2D - $2E */
Byte IOPort; /* $2F */
Byte Z80_CPU[16]; /* $30 - $3F */
Byte REC[16]; /* $40 - $4F */
Byte OSVersion; /* $50 */
Byte OSPatch; /* $51 */
Byte PatchID[2]; /* $52 - $53 */
Byte ReservedC[44]; /* $54 - $7F */
Byte Custom[128]; /* $80 - $FF */
Byte RAM[65536];
Byte IOArea[4096];
Byte REU[REUSize];
FloppyState Floppy1541[FloppyCnt];
} Snapshot;
Note: All multi-byte values are stored in Big-Endian Format (High/Low) rather than in the Little-Endian Format (Low/High) used by the C64.
Z80_CPU - Reserved for internal state of Z80 (not used)
REC - Reserved for internal state of REC (not used)
OSVersion - C64 Kernal ID byte (not used)
OSPatch - Kernal patch ID for extensions (fastloaders etc.) (not used)
PatchID - Patch version information (not used)
ReservedC - Kernal version information (not used)
Custom - Information on peripherals, external devices etc. (not used)
RAM - Contents of the RAM
IOArea - Contents of the I/O-Area (VIC, SID, ColorRAM, CIA1/2, REU)
Every device is stored only once, even so several shadows appear in the C64 address space. The gaps are filled with Power64 internal state information and lots of $00.
REU - The Contents of the REU RAM, if a RAM Expansion is present.
0, 128, 256 or 512 KByte - as specified by REUSizeKB.
Floppy1541 - The current state of each attached, completly emulated Floppy 1541.